package main

import (
	"encoding/binary"
	"encoding/json"
	"go_modbus_zutai/chores/utils"
	"log"
	"math"
	"sync"
	"time"

	"github.com/goburrow/modbus"
	"github.com/gorilla/websocket"
)

type PtStruct struct {
	Name      string      `json:"name"`
	Type      string      `json:"type"`
	Belong    string      `json:"belong"`
	Remark    string      `json:"remark"`
	Value     interface{} `json:"value"`
	Hold      int
	BoolIndex int
	ErrorCode string `json:"error_code"`
}

type ModbusReadResult struct {
	Index  int
	Values []byte
}

var ptArray = []PtStruct{}
var modbusClient modbus.Client
var modbusResults []byte
var mu sync.Mutex // 用于锁定 modbusResults 的访问

func initModbus() {
	handler := modbus.NewTCPClientHandler(modbusCfg.URL)
	handler.Timeout = 5 * time.Second
	handler.SlaveId = byte(modbusCfg.SlaveId)
	err := handler.Connect()
	if err != nil {
		log.Fatal(err)
	}
	defer handler.Close()

	// 创建 Modbus 客户端
	modbusClient = modbus.NewClient(handler)

	// 定时器
	ticker := time.NewTicker(100 * time.Millisecond)

	// 读取循环
	for range ticker.C {
		// 读取保持寄存器（寄存器地址从 0 开始）
		readModbusRegisters()

		// 处理寄存器数据
		for i, singlePt := range ptArray {
			switch singlePt.Type {
			case "BOOL":
				deal2Bool(i)
			case "WORD":
				deal2Word(i)
			case "FLOAT":
				deal2Float32(i)
			case "DINT":
				deal2Dint(i)
			case "INT":
				deal2Int(i)
			case "BYTE":
				deal2Byte(i)
			}
		}

		// 处理 WebSocket 客户端
		go handleWsClient(ptArray)

		// 错误记录
		recordErrors()
	}
}

func readModbusRegisters() {
	// 检查ptArray是否为空
	if len(ptArray) == 0 {
		log.Println("Warning: ptArray is empty")
		return
	}
	// 获取总寄存器数
	maxRegister := ptArray[len(ptArray)-1]
	totalRegisters := ptArray[len(ptArray)-1].Hold + 1
	if maxRegister.Type == "FLOAT" || maxRegister.Type == "DINT" {
		totalRegisters += 1
	}
	// for _, obj := range ptArray {
	// 	if obj.Hold > totalRegisters {
	// 		totalRegisters = obj.Hold
	// 	}
	// }
	modbusResults = make([]byte, 0, totalRegisters*2) // 每个寄存器 2 字节

	// 每次读取的寄存器块大小（假设最大为100，具体根据Modbus限制来调整）
	registerBlockSize := 100
	times := totalRegisters / registerBlockSize
	mod := totalRegisters % registerBlockSize
	// 使用通道处理读取结果，保证顺序
	times2 := times + 1
	if mod == 0 {
		times2 -= 1
	}
	resultsChan := make(chan ModbusReadResult, times2) // 通道容量为 times+1，存放读取结果

	var wg sync.WaitGroup
	for i := 0; i <= times; i++ {
		wg.Add(1)
		go func(startIndex int) {
			defer wg.Done()
			// 每次请求的寄存器范围是 [startIndex*100, (startIndex+1)*100)
			if startIndex != times || (startIndex == times && mod != 0) {
				end := 100
				if startIndex == times {
					end = mod
				}
				results, err := modbusClient.ReadHoldingRegisters(uint16(startIndex*registerBlockSize), uint16(end))
				if err != nil {
					log.Fatal(err)
				}
				// 将结果发送到通道，携带起始索引
				resultsChan <- ModbusReadResult{Index: startIndex, Values: results}
			}

		}(i)
	}

	// 等待所有并发任务完成
	wg.Wait()

	// 关闭通道
	close(resultsChan)

	// 创建一个切片来存储按顺序排列的结果
	resultsArray := make([][]byte, times+1)

	// 从通道中按顺序读取并存储数据
	for result := range resultsChan {
		resultsArray[result.Index] = result.Values
	}

	// 合并数据并拼接到 modbusResults 中
	for _, result := range resultsArray {
		mu.Lock() // 锁定对 modbusResults 的并发写操作
		modbusResults = append(modbusResults, result...)
		mu.Unlock()
	}
}

func handleWsClient(arr []PtStruct) {
	// 遍历所有 WebSocket 客户端，并发送数据
	clients.Range(func(key, value interface{}) bool {
		client, ok := value.(*wsClient) // 断言 value 为 *wsClient 类型
		if !ok {
			log.Println("Error: failed to assert client to *wsClient")
			return true // continue iterating
		}

		go func(client *wsClient) {
			// 获取该连接的锁，确保写操作是串行的
			client.mu.Lock()
			defer client.mu.Unlock()

			// 发送数据
			// 将数组转换为key-value形式的map
			eldMap := make(map[string]PtStruct)
			for _, pt := range arr {
				eldMap[pt.Name] = pt
			}

			// rstData := map[string]interface{}{
			// 	"code": 200,
			// 	"data": map[string]interface{}{
			// 		"eld": eldMap,
			// 	},
			// }

			// 将数据转为 JSON 格式
			jsonData, err := json.Marshal(eldMap)
			if err != nil {
				log.Println("JSON marshal error:", err)
				return
			}

			// 写操作
			err = client.conn.WriteMessage(websocket.TextMessage, jsonData)
			if err != nil {
				log.Println("WebSocket write error:", err)
				// 关闭无效的连接并从客户端池中删除
				client.conn.Close()
				clients.Delete(client.conn)
			}
		}(client)

		return true // continue iterating
	})
}

func deal2Bool(index int) {
	var singlePt *PtStruct = &ptArray[index]
	start := singlePt.Hold * 2
	boolArray := [16]byte{}
	for i := 0; i < 8; i++ {
		boolArray[i] = utils.MyTernary((modbusResults[start+1]&(1<<i)) != 0, byte(1), byte(0)).(byte)
		boolArray[i+8] = utils.MyTernary((modbusResults[start]&(1<<i)) != 0, byte(1), byte(0)).(byte)
	}
	singlePt.Value = boolArray[singlePt.BoolIndex]
}

func deal2Word(index int) {
	var singlePt *PtStruct = &ptArray[index]
	start := singlePt.Hold * 2
	curData := []byte{
		byte(modbusResults[start+1]),
		byte(modbusResults[start]),
	}
	value := binary.LittleEndian.Uint16(curData)
	singlePt.Value = value
}

func deal2Float32(index int) {
	var singlePt *PtStruct = &ptArray[index]
	start := singlePt.Hold * 2
	curData := []byte{
		byte(modbusResults[start]),
		byte(modbusResults[start+1]),
		byte(modbusResults[start+2]),
		byte(modbusResults[start+3]),
	}
	value := math.Float32frombits(binary.BigEndian.Uint32(curData))
	singlePt.Value = value
}

func deal2Dint(index int) {
	var singlePt *PtStruct = &ptArray[index]
	start := singlePt.Hold * 2
	curData := []byte{
		byte(modbusResults[start]),
		byte(modbusResults[start+1]),
		byte(modbusResults[start+2]),
		byte(modbusResults[start+3]),
	}
	value := binary.BigEndian.Uint32(curData)
	singlePt.Value = value
}

func deal2Int(index int) {
	// 获取当前的 PtStruct 指针
	var singlePt *PtStruct = &ptArray[index]
	// 计算数据在 modbusResults 中的起始位置
	start := singlePt.Hold * 2
	// 提取当前寄存器中的两个字节数据
	curData := []byte{
		byte(modbusResults[start+1]),
		byte(modbusResults[start]),
	}
	// 将字节数据转换为 INT（16位）
	value := binary.LittleEndian.Uint16(curData)
	// 如果需要将结果扩展为有符号的 INT，可以进行类型转换
	singlePt.Value = int16(value)
}
func deal2Byte(index int) {
	// 获取当前的 PtStruct 指针
	var singlePt *PtStruct = &ptArray[index]
	// 计算数据在 modbusResults 中的起始位置
	start := singlePt.Hold * 2
	// 提取当前寄存器中的两个字节数据
	curData := []byte{
		byte(modbusResults[start+1]),
		byte(modbusResults[start]),
	}
	// // 将字节数据转换为 INT（16位）
	// value := binary.LittleEndian.Uint16(curData)
	// 如果需要将结果扩展为有符号的 INT，可以进行类型转换
	singlePt.Value = curData
}

func recordErrors() {
	for _, singlePt := range ptArray {
		if singlePt.Belong == "error" {
			updateError(singlePt)
		}
	}
}
